/** \file   ReadHDF5VarLen.hpp
 *  \author Jason Kraftcheck
 *  \date   2010-09-04
 */

#ifndef moab_READ_HDF5_VAR_LEN_HPP
#define moab_READ_HDF5_VAR_LEN_HPP

#include "moab/MOABConfig.h"
#ifdef MOAB_HAVE_MPI
#include "moab_mpi.h"
#endif
#include "DebugOutput.hpp"
#include "moab/Range.hpp"
#include "H5Ipublic.h"

namespace moab
{

class ReadHDF5Dataset;

/**\brief Read variable-length data from 1-D array dataset
 *
 * Utility class for reading variable-length data from an HDF5 dataset.
 * Used for reading set contents, set parents, set children,
 * polygon and polyhedron connectivity, and variable-length tag
 * data.
 *
 * This is an abstract class.  The pure virtual \c store_data method
 * must be implemented to create a concrete instance.
 */
class ReadHDF5VarLen
{
  protected:
    DebugOutput& dbgOut;

  private:
    void* const dataBuffer;
    const size_t bufferSize;

    /**\brief Test if passed file_id is value pointed to by ranged_iter,
     *        and if so, incremenet ranged_iter
     */
    static bool is_ranged( EntityHandle file_id, Range::const_iterator& ranged_iter, Range::const_iterator ranged_end );

  protected:
    /**\brief Store data list for a single entity
     *
     * The is the pure virtual method that must be provided.
     * It is responsible for storing the data read for a single
     * entity.
     *
     * This function will always be called in the order of the
     * file_ids in the range passed to the \c read method.
     *
     *\param file_id  The file ID for the entity
     *\param data     A pointer to the data for the entity
     *\param num_data Number of values for the entity
     *\param ranged   For set contents, true if in ranged format.
     */
    virtual ErrorCode store_data( EntityHandle file_id, void* data, long num_data, bool ranged ) = 0;

  public:
    /**\brief Constructor
     *\param buffer      A temporary buffer to use during read
     *\param buffer_size Size of \c buffer, in bytes.
     */
    ReadHDF5VarLen( DebugOutput& debug_output, void* buffer, size_t buffer_size )
        : dbgOut( debug_output ), dataBuffer( buffer ), bufferSize( buffer_size )
    {
    }

    virtual ~ReadHDF5VarLen() {}

    /**\brief Do actual read of data set
     *\param data_set         The data set to read.
     *\param file_ids         The file ids of the entities to read.
     *\param start_file_id    The file id corresponding to the first row of the dataset
     *\param data_type        The desired, in-memory data type for values
     *\param vals_per_ent     The number of values for each entity
     *\param ranged_file_ids  Those file ids for which the 'ranged'
     *                        argument to \c storedata should be passed
     *                        as \c true.
     */
    ErrorCode read_data( ReadHDF5Dataset& data_set,
                         const Range& offsets,
                         EntityHandle start_offset,
                         hid_t data_type,
                         const Range& file_ids,
                         const std::vector< unsigned >& vals_per_ent,
                         const Range& ranged_file_ids );

    /**\brief Read set description table or offset vector for
     *        var-len tags or old-format poly(gon|hedra) connectivity.
     *\param data_set         The data set to read.
     *\param file_ids         The file ids of the entities to read.
     *\param start_file_id    The file id corresponding to the first row of the dataset
     *\param num_columns      The number of columns of offsets in the dataset
     *\param indices          Array of length \c num_columns contaning the
     *                        indices of the columns to read.
     *\param nudge            Amount by which to offset values in
     *                        \c offset_out to avoid putting zeros in
     *                        Range.  Must be greater than 0.  Probably 1.
     *\param offsets_out      An array of length \c num_columns which will
     *                        be populated with the resulting list of offsets
     *                        into the contents list calculated from reading
     *                        the offsets from the passed data set.
     *\param counts_out       An array of length \c num_columns of std::vectors,
     *                        where each vector will be filled with one value
     *                        per file ID indicating the length of the data for
     *                        the corresponding file ID.
     *\param ranged_file_ids  If non-null, the last column of the table will
     *                        be read and tested for the ranged bit.  For
     *                        all file_ids for which the range bit is set,
     *                        the file ID will be added to this list.
     */
    /*
       ErrorCode read_offsets( ReadHDF5Dataset& data_set,
                               const Range& file_ids,
                               EntityHandle start_file_id,
                               unsigned num_columns,
                               const unsigned indices[],
                               EntityHandle nudge,
                               Range offsets_out[],
                               std::vector<unsigned> counts_out[],
                               Range* ranged_file_ids = 0 );
   */
    ErrorCode read_offsets( ReadHDF5Dataset& data_set,
                            const Range& file_ids,
                            EntityHandle start_file_id,
                            EntityHandle nudge,
                            Range& offsets_out,
                            std::vector< unsigned >& counts_out );

    ErrorCode read( ReadHDF5Dataset& offset_data,
                    ReadHDF5Dataset& value_data,
                    const Range& file_ids,
                    EntityHandle start_file_id,
                    hid_t data_type,
                    const Range* ranged = 0 )
    {
        ErrorCode rval;
        const EntityHandle nudge = 1;
        Range offsets;
        std::vector< unsigned > counts;
        rval = read_offsets( offset_data, file_ids, start_file_id, nudge, offsets, counts );
        if( MB_SUCCESS != rval ) return rval;
        Range empty;
        rval = read_data( value_data, offsets, nudge, data_type, file_ids, counts, ranged ? *ranged : empty );
        return rval;
    }
};

}  // namespace moab

#endif  // moab_READ_HDF5_VAR_LEN_HPP
